Borland Online And The Cobb Group Present:


March, 1995 - Vol. 2 No. 3

Extending objectwindows 2.0 - Enabling other controls after data validation

In Automatically verifying data entries with picture validators, we describe how you use validators to help users format data in an edit control. In most situations, this form of entry validation works well.

However, if you want to clearly indicate to the user when the data is valid or if you need to force the user to enter data in a certain order, you'll want to use a different approach. In this article, we'll show how you can enable and disable a dialog box control based on the validity of the data in an edit control.

Are validators a valid approach?

At first, you might think you should derive a new validator class from one of the existing filter validators. To do this, you'd simply override the IsValid() member function in the validator class so that it calls the TWindow::EnableWindow() member function of the control you want to enable or disable.

Unfortunately, this doesn't work very well because of the way a TEdit object interacts with its corresponding validator. (To learn the details, read How validator objects interact with edit controls below.) If you try this approach, you'll find that the validator will attempt to enable the control when the edit data doesn't appear to be valid, and it will sometimes stay disabled when the edit data does appear valid.

Subclassing TEdit

Instead of deriving a new validator class to enable and disable the associated control, we'll derive a new type of interface object for edit controls from the existing TEdit class. This way, we'll be able to customize how the edit control's interface object calls the validator's Valid() and IsValid() member functions.

This approach is important because the TEdit class doesn't call either of these functions when the user enters a character. If you override the TEdit::EvChar() function in such a way that it processes all the data in the edit control (including the current key press) prior to calling the TEdit::IsValid() function, the TEdit::IsValid() function will, in turn, call either the validator's IsValid() or Valid() function.

Keep in mind that when you derive a new message-handling class from an existing one, you still need to declare a message response table for the new class. You also have to provide an entry in its message response table definition for the message you want the new class to handle. For the TEdit class, you want to override the EvChar() function, so you must include the WM_CHAR message response macro (EV_WM_CHAR) in the response table definition.

Edit control enabling and disabling

Now let's create a simple program that uses a TEdit-derived class to enable and disable a control based on the validity of the data in an edit control. This program is similar in structure to the example in Automatically verifying data entries with picture validators but we've made some changes in order to simplify the program and clarify the use of the new TControlEnabler class.

To begin, launch the Borland C++ 4.0 Integrated Development Environment (IDE) and choose New Project... from the Project menu. In the New Project dialog box, enter \CTL_ENBL\CTL_ENBL.IDE in the Project Path And Name entry field, set the remaining options for a Windows application that uses the ObjectWindows Library (OWL ) standard library, and click OK.

When the new Project window appears, double-click on the name ctl_enbl [.cpp]. When the editing window for this file appears, enter the code from Listing A.


Listing A: CTL_ENBL.CPP

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\owlpch.h>
#include <owl\eventhan.h>
#include <owl\edit.h>
#include <owl\button.h>
#include <owl\validate.h>

class TControlEnabler : public TEdit
{
  public:
  TControlEnabler(TWindow* parent,
           int resourceId,
           UINT length,
           TWindow* control) :
    TEdit(parent, resourceId, length),
    control(control) {}

  protected:
   void EvChar(UINT key, UINT repeat, UINT flags)
   { TEdit::EvChar(key, repeatCount, flags);
     if(GetTextLen() && IsValid(FALSE))
     {
      if(control)
        control->EnableWindow(TRUE);
     }
     else
     { if(control)
        control->EnableWindow(FALSE);
     }
   }
  private:
   TWindow* control;
  DECLARE_RESPONSE_TABLE(TControlEnabler);
};

DEFINE_RESPONSE_TABLE1(TControlEnabler, TEdit)
  EV_WM_CHAR,
END_RESPONSE_TABLE;

class TCtlEnblApp : public TApplication
{
  public:
  TCtlEnblApp() {}
  ~TCtlEnblApp() {}

  void InitMainWindow()
  { TFrameWindow* frame =
    new TFrameWindow(0, "Control Enabler");
   frame->AssignMenu(1);
   SetMainWindow(frame); 
  }
  void DisplayDialog()
  { TDialog* d =
      new TDialog(GetMainWindow(), 1000);
    TControlEnabler* e = 
        new TControlEnabler(d, 1100, 20,
                        new TButton(d, IDOK));
     e->SetValidator(new 
           TPXPictureValidator("(###) ###-####", 
                               FALSE));
    d->Execute();
    d->Destroy();
  }

  DECLARE_RESPONSE_TABLE(TCtlEnblApp);
};

DEFINE_RESPONSE_TABLE1(TCtlEnblApp, TApplication)
  EV_COMMAND(101, DisplayDialog),
END_RESPONSE_TABLE;

int OwlMain(int, char**)
{ return TCtlEnblApp().Run(); }


When you finish entering the code, choose Save from the File menu. Then, right-click on the name ctl_enbl [.rc], choose View from the pop-up menu, and then choose Text Edit from the View submenu. In the editing window that appears, enter the resource file from Listing B.


Listing B: CTL_ENBL.RC

#include <owl\validate.rc>

1000 DIALOG 34, 25, 104, 62
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Enter Phone #"
{
 EDITTEXT 1100, 11, 15, 60, 11
 DEFPUSHBUTTON "OK", IDOK, 25, 39, 50, 14, 
   WS_DISABLED 
}

1 MENU
{
  POPUP "Menu"
  {
	 MENUITEM "Command", 101
  }
}


When you finish entering the resource file, choose Save from the File menu. To close this editing window, double-click on its System menu icon. To allow the IDE to use the default Module definition file, right-click on ctl_enbl [.def], and then choose Delete Node from the pop-up menu.

Now, choose Run from the Debug menu to build and run this application. When the application's window appears, choose Command from the Menu menu.

When the Enter Phone # dialog box appears, enter (555) 555-. You'll notice that the application disables the OK button when the dialog box first appears, as shown in Figure A. Next, enter 5555. As soon as you enter the last 5, you'll see the program enable the OK button, as shown in Figure B.


Figure A - When you begin entering data in an edit field associated with a TControlEnabler object, it will disable its associated control.

Notice that if you delete characters from the edit field, the program disables the OK button again. When the OK button is available, click it to close the dialog box. When the Control Enabler program window reappears, double-click on its System menu icon to close the application.


Figure B - As soon as you enter valid data, the TControlEnabler object will enable its associated control.

Out in the code

The reason this technique works is that the TControlEnabler class checks the validity of the data in the edit control each time the user enters a character that the TEdit class would normally process in the EvChar() member function. If you look closely at the TControlEnabler::EvChar() member function, you'll see that it calls the TEdit version of the function before it makes the call to the TEdit::IsValid() function.

In addition, the second line of the function,

if(GetTextLen() && IsValid(FALSE))

calls the GetTextLen() function to make sure the edit field isn't empty. (By default, the TPXPictureValidator class considers an empty edit field to be valid.)

If the field isn't empty and if the data in the field is valid, we enable the control by using the EnableWindow() function. Otherwise, we disable it by using the same function.

Conclusion

The TEdit class and the corresponding validator classes that Borland ships with Borland C++ provide powerful tools for validating input data. By understanding how these elements work together at run-time, you can extend the behavior of these classes to perform run-time enabling and disabling of a separate Windows control.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.